package cn.tqfeiyang.springplus.system.service.impl;

import cn.tqfeiyang.springplus.system.entity.UserEntity;
import cn.tqfeiyang.springplus.system.mapper.UserMapper;
import cn.tqfeiyang.springplus.system.mapper.RoleMapper;
import cn.tqfeiyang.springplus.system.mapper.UserRoleMapper;
import cn.tqfeiyang.springplus.system.request.UserSTO;
import cn.tqfeiyang.springplus.system.service.UserService;
import cn.tqfeiyang.springplus.system.request.UserDTO;
import cn.tqfeiyang.springplus.system.response.UserVO;
import cn.tqfeiyang.springplus.system.response.RoleVO;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.logging.log4j.util.Strings;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.tqfeiyang.springplus.framework.conversion.ConversionService;
import cn.tqfeiyang.springplus.framework.exception.BusinessException;
import cn.tqfeiyang.springplus.framework.response.ResultCode;
import cn.tqfeiyang.springplus.framework.pagination.PageHelper;
import cn.tqfeiyang.springplus.framework.pagination.PageData;
import jakarta.annotation.Resource;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 用户服务实现类
 *
 * @author tqfeiyang
 * @since 2024-08-01
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {

    /**
     * 对象转换服务
     */
    @Resource
    private ConversionService conversionService;

    /**
     * 用户映射接口
     */
    @Resource
    private UserMapper userMapper;

    /**
     * 角色映射接口
     */
    @Resource
    private RoleMapper roleMapper;

    /**
     * 用户/角色关联接口
     */
    @Resource
    private UserRoleMapper userRoleMapper;

    /**
     * 密码加密器
     */
    @Resource
    private PasswordEncoder passwordEncoder;

    /**
     * 分页查询用户
     *
     * @param userSTO 查询数据
     * @return 用户列表
     */
    @Override
    public PageData<UserVO> selectUserPage(UserSTO userSTO) {
        Assert.notNull(userSTO, "userSTO不能为空");
        PageHelper<UserEntity> pageHelper = new PageHelper<>(userSTO);
        return pageHelper.selectPage(userMapper, UserVO.class);
    }

    /**
     * 查询用户
     *
     * @param userId 用户id
     * @return 用户信息
     */
    @Override
    public UserVO selectUserInfo(Long userId) {
        Assert.notNull(userId, "userId不能为空");
        UserEntity userEntity = checkUserExists(userId);
        return conversionService.map(userEntity, UserVO.class);
    }

    /**
     * 新建用户
     *
     * @param userDTO 请求数据
     * @return 用户id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createUser(UserDTO userDTO) {
        Assert.notNull(userDTO, "userDTO不能为空");
        UserEntity userEntity = conversionService.map(userDTO, UserEntity.class);

        // 如果请求提供了密码，则需要做加密处理
        // 如果没有提供密码，则保存为空，意思是可以用空密码登录
        if (StringUtils.isNotBlank(userEntity.getPassword())) {
            userEntity.setPassword(passwordEncoder.encode(userEntity.getPassword()));
        } else {
            userEntity.setPassword(Strings.EMPTY);
        }

        // 如果请求没有提供UUID，则自动生成一个
        if (StringUtils.isBlank(userEntity.getUuid())) {
            userEntity.setUuid(UUID.randomUUID().toString());
        }

        if (userMapper.insert(userEntity) == 0) {
            throw new BusinessException(ResultCode.ENTITY_INSERT_ERROR);
        }
        if (userDTO.getRoles() != null) {
            assignRoleIds(userEntity.getUserId(), userDTO.getRoles());
        }
        return userEntity.getUserId();
    }

    /**
     * 修改用户
     *
     * @param userId 用户id
     * @param userDTO 请求数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(Long userId, UserDTO userDTO) {
        Assert.notNull(userId, "userId不能为空");
        Assert.notNull(userDTO, "userDTO不能为空");
        UserEntity originalEntity = checkUserExists(userId);
        UserEntity userEntity = conversionService.map(userDTO, UserEntity.class);
        userEntity.setUserId(userId);
        userEntity.setVersion(originalEntity.getVersion());

        // 如果请求提供了密码，则需要做加密处理
        if (StringUtils.isNotBlank(userEntity.getPassword())) {
            userEntity.setPassword(passwordEncoder.encode(userEntity.getPassword()));
        }

        if (userMapper.updateById(userEntity) == 0) {
            throw new BusinessException(ResultCode.ENTITY_UPDATE_ERROR);
        }
        if (userDTO.getRoles() != null) {
            assignRoleIds(userId, userDTO.getRoles());
        }
    }

    /**
     * 删除用户
     *
     * @param userId 用户id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUser(Long userId) {
        Assert.notNull(userId, "userId不能为空");
        checkUserExists(userId);
        if (userMapper.deleteById(userId) == 0) {
            throw new BusinessException(ResultCode.ENTITY_DELETE_ERROR);
        }
    }

    /**
     * 检查用户实体是否存在
     *
     * @param userId 用户id
     * @return 用户实体
     */
    protected UserEntity checkUserExists(Long userId) {
        UserEntity userEntity = userMapper.selectById(userId);
        if (userEntity == null) {
            throw new BusinessException(ResultCode.ENTITY_NOT_EXISTS);
        }
        return userEntity;
    }

    /**
     * 查询角色
     *
     * @param userId
     * @return 角色列表
     */
    @Override
    public List<RoleVO> selectRoleList(Long userId) {
        Assert.notNull(userId, "userId不能为空");
        checkUserExists(userId);
        List<Long> roleIds = userRoleMapper.selectRoleIdsByUserId(userId);
        return roleMapper.selectBatchIds(roleIds).stream()
                .map(roleEntity -> conversionService.map(roleEntity, RoleVO.class))
                .toList();
    }

    /**
     * 分配角色
     *
     * @param userId  用户id
     * @param roleIds 角色集合
     */
    private void assignRoleIds(Long userId, Set<Long> roleIds) {
        if (CollectionUtils.isNotEmpty(roleIds)) {
            List<Long> original = userRoleMapper.selectRoleIdsByUserId(userId);
            List<Long> inserted = roleIds.stream()
                    .filter(item -> !original.contains(item))
                    .collect(Collectors.toList());
            List<Long> deleted = original.stream()
                    .filter(item -> !roleIds.contains(item))
                    .collect(Collectors.toList());
            if (!inserted.isEmpty()) {
                int result = userRoleMapper.insertRoleIdsByUserId(userId, inserted);
                if (result != inserted.size()) {
                    throw new BusinessException(ResultCode.ENTITY_INSERT_ERROR);
                }
            }
            if (!deleted.isEmpty()) {
                int result = userRoleMapper.deleteRoleIdsByUserId(userId, deleted);
                if (result != deleted.size()) {
                    throw new BusinessException(ResultCode.ENTITY_DELETE_ERROR);
                }
            }
        } else {
            userRoleMapper.deleteAllRoleIdsByUserId(userId);
        }
    }

}
